import * as THREE from 'three'

import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader';
import { Line2 } from "three/examples/jsm/lines/Line2.js";
import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
import { LineGeometry } from "three/examples/jsm/lines/LineGeometry.js";
import { unreal } from '../utils';


import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

import TWEEN, { Tween } from '@tweenjs/tween.js'
import { ResourceTracker } from './js/ResourceTracker';


export function init() {
 
// 场景
let scene = new THREE.Scene()
// scene.background = new THREE.Color('#dddddd');

// 尺寸
let width = window.innerWidth;
let height = window.innerHeight;

// 相机
let camera = new THREE.PerspectiveCamera(45, width / height, 1, 2000);
camera.position.set(83, 62, 104)

// 灯光组
const lightGroup = new THREE.Group()

// 平行光
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
lightGroup.add(directionalLight);/*  */

// 环境光
const AmbientLight = new THREE.AmbientLight(0xffffff, 1.5);
lightGroup.add(AmbientLight);

// 渲染器
let renderer:any=null;

const canvas = document.querySelector('#threeMain')
if (canvas) {
    renderer = new THREE.WebGLRenderer({ // 渲染器
        antialias: true, //抗锯齿
        canvas: canvas,
        alpha: true, // canvas是否包含alpha (透明度) 默认为 false
    })
    renderer.setClearColor(0x000000,0); 
    renderer.setSize(width, height)
    // 轨道控制器
}
let controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', () => {
    // console.log(camera.position);

})
scene.add(lightGroup)   

// scene.background = new THREE.Color(0x000000)


const bloomLayer = new THREE.Layers();
const BLOOM_SCENE = 1;
bloomLayer.set(BLOOM_SCENE);

const params = {
    threshold: 0.349,
    strength: 0.567, // 强度
    radius: 0,// 半径
    exposure: 1.55 // 扩散
};

// 获取发光场景必要元素
const { finalComposer: F,
    bloomComposer: B,
    renderScene: R, bloomPass: BP } = unreal(scene, camera, renderer, width, height, params)
let finalComposer = F
let bloomComposer = B
let renderScene = R
let bloomPass = BP

bloomPass.threshold = 0

const materials = {}

const logoLineResMgr = new ResourceTracker();
const track = logoLineResMgr.track.bind(logoLineResMgr)

const biuResMgr = new ResourceTracker();
const biuTrack = biuResMgr.track.bind(biuResMgr)

const moreResMgr = new ResourceTracker();
const moreTrack = moreResMgr.track.bind(moreResMgr)

const svgLoader = new SVGLoader()

const darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' });

let render = () => {
    controls.update()
    camera.updateProjectionMatrix()
    renderer.render(scene, camera)
    TWEEN && TWEEN.update();

    if (bloomComposer) {
        
        scene.traverse(darkenNonBloomed.bind(this));
        bloomComposer.render();
    }
    if (finalComposer) {
        scene.traverse(restoreMaterial.bind(this));
        finalComposer.render();
    }
    
}

function darkenNonBloomed(obj: THREE.Mesh) {
    if (bloomLayer) {
        if (!obj.userData.isLight && bloomLayer.test(obj.layers) === false) {
            materials[obj.uuid] = obj.material;
            obj.material = darkMaterial;

        }
    }

}

function restoreMaterial(obj: THREE.Mesh) {

    if (materials[obj.uuid]) {

        obj.material = materials[obj.uuid];
        // 用于删除没必要的渲染
        delete materials[obj.uuid];

    }

}

// 用于制作行动轨迹的点位合集
let paths: THREE.Vector3[][] = []
// 用于计算box3的点位合集
let divisionPoints: THREE.Vector2[] = []

// 用于创建logo挤压模型的形状Shape
const logoShape = new THREE.Shape()
// 用于创建多余部分的挤压模型形状
const moreShape = new THREE.Shape()

// 加载模型
const loadModel = async () => {
    let mode = import.meta.env.MODE
    const base = `svg/orange.svg`
    //根据环境变量，判断是开发环境还是生产环境，生成的路径不一样，
    let jsonPath = mode === 'development' ? '/' + base : '../' + base

    svgLoader.load(jsonPath, (data:any) => {
      
        if(data.paths.length === 0){
            console.log('err获取svg文件失败')
            return
        }

        for (const path of data.paths) {
            const shapes = SVGLoader.createShapes(path);

            for (const shape of shapes) {
                for (const curve of shape.curves) {
                    let list: THREE.Vector3[] = []
                    /*
                     * .getPoints ( divisions : Integer ) : Array
                     * divisions -- 要将曲线划分为的分段数。默认是 5.
                     */
                    const length = curve.getLength();

                    const points = curve.getPoints(Math.floor(length / 20));
                    for (let i = 0; i < points.length - 1; i++) {
                        const v2 = points[i]
                        if (v2.x !== 0 && v2.x && v2.y !== 0 && v2.y) {
                            // logo 太大了，缩小一下，这里不建议用scale缩svg，直接缩向量，后面依赖向量的元素都需要重新绘制
                            v2.divideScalar(20)
                            const v3 = new THREE.Vector3(v2.x, 0, v2.y)
                            list.push(v3)
                            divisionPoints.push(v2)
                            if (i === 0) {
                                logoShape.moveTo(v2.x, v2.y)
                            } else {
                                logoShape.lineTo(v2.x, v2.y)
                            }
                        }
                    }

                    paths.push(list)
                }

            }

        }


        handlePaths()
        renderer.setAnimationLoop(render)
    })
}


const logoSize = new THREE.Vector2()
const logoCenter = new THREE.Vector2()
// 底板厚度
const floorHeight = 3
let floor: THREE.Mesh | null
// 底板比logo的扩张尺寸
let floorOffset = 14

let logoLine

// logo和底板的材质
const logoMaterial = new THREE.MeshLambertMaterial({ color: 0xff4500, side: THREE.DoubleSide });
// 实体logo
let logoMesh = new THREE.Mesh()
// 多余部分的实体
let moreMesh = moreTrack(new THREE.Mesh())

const handlePaths = () => {
    const box2 = new THREE.Box2();
    box2.setFromPoints(divisionPoints)
    box2.getSize(logoSize)
    box2.getCenter(logoCenter)
    createFloor()
}

const createFloor = () => {
    const floorSize = logoSize.clone().addScalar(floorOffset)
    const geometry = new THREE.BoxGeometry(floorSize.width, floorHeight, floorSize.height);

    floor = new THREE.Mesh(geometry, logoMaterial);
    // scene.add(floor);

    moreShape.moveTo(floorSize.x / 2, floorSize.y / 2);
    moreShape.lineTo(-floorSize.x / 2, floorSize.y / 2);
    moreShape.lineTo(-floorSize.x / 2, -floorSize.y / 2);
    moreShape.lineTo(floorSize.x / 2, -floorSize.y / 2);


    const path = new THREE.Path()

    const logoPos = new THREE.Vector3(logoCenter.x, -floorHeight / 2, logoCenter.y).negate()

    // logo实例
    logoMesh = createLogoMesh(logoShape)
    logoMesh.position.copy(logoPos.clone())
    scene.add(logoMesh);

    // 孔洞path
    divisionPoints.forEach((point, i) => {
        point.add(logoCenter.clone().negate())
        if (i === 0) {
            path.moveTo(point.x, point.y);
        } else {
            path.lineTo(point.x, point.y);
        }
    })
    // 多余部分添加孔洞
    moreShape.holes.push(path)
    // 多余部分实例
    moreMesh = createLogoMesh(moreShape)
    moreMesh.position.setY(floorHeight / 2)
    scene.add(moreMesh)

    createBiu()
}

const createLine = () => {
    const material = new THREE.LineBasicMaterial({
        color: 0x0000ff
    });

    const geometry = new THREE.BufferGeometry().setFromPoints([]);

    let logoLine = new THREE.Line(geometry, material);
    return logoLine
}

// 激光组
const biuGroup = biuTrack(new THREE.Group())

// logo切割线组
const logoLineGroup = track(new THREE.Group())

scene.add(biuGroup)
scene.add(logoLineGroup)
// 激光起点相对于logo缩进的位置
const biuDivide = 2
// 决定激光起点距离场景中心的距离
const biuOffsetH = 30
// 决定有几条激光
const biuCount = 4

const createBiu = () => {
    // 创建一个圆弧，将来如果有很多激光，那么起点就从圆弧的点位上取
    var R = Math.min(...logoSize.toArray()) / biuDivide; //圆弧半径
    var N = Math.pow(biuCount, 2); // 根据激光的条数生成圆弧上的点位数量
    // 批量生成圆弧上的顶点数据
    const vertices: number[] = []
    for (var i = -N / 2; i < N / 2; i++) {
        var angle = 2 * Math.PI / N * i;
        var x = R * Math.sin(angle);
        var y = R * Math.cos(angle);
        vertices.push(x, biuOffsetH, y)
    }

    // 创建圆弧的辅助线
    // initArc(vertices)

    for (let i = 0; i < biuCount; i++) {

        const startPoint = new THREE.Vector3().fromArray(vertices, i * biuCount * 3)
        const endPoint = new THREE.Vector3()

        endPoint.copy(startPoint.clone().setY(-floorHeight))
        // 创建cube辅助块
        // const color = new THREE.Color(Math.random() * 0xffffff)
        // initCube(startPoint, color)
        // initCube(endPoint, color)
        createLine2([...startPoint.toArray(), ...endPoint.toArray()], startPoint)
    }
    biuAnimate()
}

loadModel()

const createLine2 = (linePoints: number[], startPoint: THREE.Vector3) => {
    const geometry = new LineGeometry();
    geometry.setPositions(linePoints);
    const matLine = new LineMaterial({
        linewidth: 0.002, // 可以调整线宽
        dashed: true,
        opacity: 0.5,
        color: 0x4cb2f8,
        vertexColors: false, // 是否使用顶点颜色
    });

    let biu = new Line2(geometry, matLine);
    biu.userData.startPoint = startPoint
    // biu['isLight'] = true
    biu.userData.isLight = true
    biuGroup.add(biu);
}

const biuAnimate = () => {
    // biuCount
    
    // todo 这里要改成points这样的 每次切割完 收缩一下激光，再伸展出来，目前更新激光点位功能还没开发出来
    const allPoints = [...divisionPoints]
    const len = Math.ceil(allPoints.length / biuCount)


    for (let i = 0; i < biuCount; i++) {
        const points = allPoints.splice(0, len);
        
        // allPoints是截取到上一轮点位的其余点位，所以第一个就是当前激光相邻的第一个点
        if (i < biuCount - 1) {
            points.push(allPoints[0])
        } else {
            points.push(divisionPoints[0])
        }
        const biu = biuGroup.children[i] as Line2;
        const biuStartPoint = biu.userData.startPoint
        let j = 0;

        const line = createLine()
        logoLineGroup.add(line)
        let flag = true
        let tweenStart = new THREE.Vector3()
        const interval = setInterval(() => {
            const point = points[j]
            const tweenRun = (end: THREE.Vector3, start: THREE.Vector3, time = 400) => {
       
                new TWEEN.Tween(end)
                    .to(start, time)
                    .start()
                    .onUpdate((v: any) => {
                        const attrPosition = [...biuStartPoint.toArray(), ...v.toArray()]
                        
                        uploadBiuLine(biu, attrPosition)
                    })
                    .onComplete(() => {

                        flag = true
                        j++
                    })
            }
            if (j < points.length && point) {

                const tweenFun = () => {
                    const endPoint = new THREE.Vector3(point.x, floorHeight / 2, point.y)
                    tweenStart.copy(endPoint.clone())
                    const logoLinePointArray = [...(line.geometry.attributes['position']?.array || [])];

                    logoLinePointArray.push(...endPoint.toArray())

                    line.geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(logoLinePointArray), 3))


                    if (j % 20 === 0 && j !== 0) {
                        flag = false
                        tweenRun(endPoint, biuStartPoint)

                    } else {
                        const attrPosition = [...biuStartPoint.toArray(), ...endPoint.toArray()]
                        
                        uploadBiuLine(biu, attrPosition)

                        j++
                    }
                }
                if (flag) {

                    tweenFun()
                }

            } else {
                if( i === biuCount-1) {
                    console.log(2)
                    tweenRun(biuStartPoint, tweenStart, 0)
                    logoLineResMgr.dispose();
                    biuResMgr.dispose();
                    new TWEEN.Tween(moreMesh.position)
                        .to(moreMesh.position.clone().setY(40), 1000)
                        .start()
                        .easing(TWEEN.Easing.Quadratic.InOut)
                        .onComplete(() => {
                            new TWEEN.Tween(moreMesh.position)
                                .to(moreMesh.position.clone().setX(400), 1000)
                                .start()
                                .onComplete(() => {
                                    moreResMgr.dispose()
                                    new TWEEN.Tween(camera.position)
                                        .to(new THREE.Vector3(0, 128, 0), 500)
                                        .start()
                                        .onComplete(()=>{
                                            logoMesh.userData.isLight = true
                                        })
                                })
                        })

                }

                clearInterval(interval)
            }
        }, 100)


    }
}


// 更新激光信息
const uploadBiuLine = (line2: Line2, attrPosition: number[]) => {
  
    const geometry = new LineGeometry();
    line2.geometry.setPositions(attrPosition);
}

// 创建模型
const createLogoMesh = (shape: THREE.Shape) => {

    const extrudeSettings = {
        depth: floorHeight,
        bevelEnabled: false
    };

    const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
    const shapeMesh = new THREE.Mesh(geometry, logoMaterial);
    shapeMesh.rotation.x = Math.PI * 0.5

    return shapeMesh
}


}